package com.xingyun.kingdee.erp.service.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xingyun.kingdee.erp.common.model.param.base.AbstractErpParam;
import com.xingyun.kingdee.erp.common.model.param.base.ErpLoginParam;
import com.xingyun.kingdee.erp.service.common.config.ErpUrlConstants;
import com.xingyun.kingdee.erp.service.common.config.redis.XyRedisManager;
import com.xingyun.kingdee.erp.service.common.enums.ErpLoginResultCodeEnum;
import com.xingyun.kingdee.erp.service.common.exceptions.ErpAccessException;
import com.xingyun.kingdee.erp.service.common.exceptions.ErpLoginFailException;
import com.xingyun.kingdee.erp.service.common.exceptions.ErpResponseNotMeetExpectation;
import com.xingyun.kingdee.erp.service.model.LoginResponseBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;

/**
 * @author lingo
 * @version v1.0
 * @date 2019/11/15 9:37
 * @Company www.xyb2b.com
 */
@Component
public class AccessErpClient {

    private final static Logger logger = LoggerFactory.getLogger(AccessErpClient.class);
    private final static String SUBMITED_FLAG = "无需重复提交!";

    private static final String[] COOKIE_EXPIRE_FLAG_LIST_WHEN_SINGLE_SAVE = new String[]{"Context Is Null","重新登录"};
    private static final String COOKIE_EXPIRE_GENERAL_FLAG = "response_error:Code(-2147467259);";
    private static final List<String> BILL_DELETED_FLAG_LIST = Arrays.asList( "不存在,可能已经被删除","传递的编码值不存在");
    private static final String VIEW_LIST_ERROR_FLAG = "\"IsSuccess\":false";
    private static final String UNAUDIT_NOT_EXISTS_FLAG = "不存在";
    private static final String[] UNAUDIT_SUCCESS_FLAG =  new String[]{UNAUDIT_NOT_EXISTS_FLAG,"请先提交审核"};
    private static final String[] DELETE_SUCCESS_FLAG =  new String[]{"不存在"};


    private static RestTemplate restTemplate;

    private static final long ONE_HOUR = 60 * 60;

    @Autowired
    private XyRedisManager xyRedisManager;

    /**
     * 默认 半天
     */
    private Long redisCookieExpireTime;

    @Value("${erp.cookieExpireTime:12}")
    public void setRedisCookieExpireTime(Long redisCookieExpireTime) {
        this.redisCookieExpireTime = redisCookieExpireTime * ONE_HOUR;
    }

    static {
        restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("utf-8")));
    }

    private List<String> login(ErpLoginParam loginParam, boolean isGetFromRedis) {
        List<String> cookie;
        if (isGetFromRedis) {
            cookie = this.getCookieFromRedis(loginParam);
            if (CollectionUtils.isEmpty(cookie)) {
                logger.info("Redis中无账号 {} 访问数据中心 {} 所需的cookie，正在重新登录", loginParam.getUsername(), loginParam.getAcctId());
                cookie = this.loginToGetCookie(loginParam);
            } else {
                logger.info("Redis中有账号 {} 访问数据中心 {} 所需的cookie，直接进行操作", loginParam.getUsername(), loginParam.getAcctId());
            }
        }else{
            cookie = this.loginToGetCookie(loginParam);
        }
        return cookie;
    }

    private HttpHeaders getJsonHeader(List<String> cookieList){
        HttpHeaders nowHeader = new HttpHeaders();
        nowHeader.add(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
        if (!CollectionUtils.isEmpty(cookieList)){
            cookieList.forEach(aCookie -> nowHeader.add(HttpHeaders.COOKIE, aCookie));
        }
        return nowHeader;
    }

    private String getLoginParamKey(ErpLoginParam erpLoginParam){
        return String.format("%s+%s",erpLoginParam.getUsername(), erpLoginParam.getAcctId());
    }

    private List<String> getCookieFromRedis(ErpLoginParam loginParam) {
        String redisKey = getLoginParamKey(loginParam);
        Object value = xyRedisManager.get(redisKey);
        List<String> resultList = null;
        if (value != null){
            resultList = JSONObject.parseArray(String.valueOf(value)).toJavaList(String.class);
        }
        return resultList;
    }

    private void setCookieToRedis(ErpLoginParam loginBaseParam, List<String> cookieList) {
        boolean isSuccess = xyRedisManager.set(getLoginParamKey(loginBaseParam), JSON.toJSONString(cookieList), redisCookieExpireTime);
        if (isSuccess){
            logger.info("账号:{}登录erp数据中心 {} 成功的cookie已写入Redis中",loginBaseParam.getUsername(),loginBaseParam.getAcctId());
        }else {//就算设置失败，也要成功
            logger.info("账号:{}登录erp数据中心 {} 成功的cookie无法写入Redis中，key: {}",loginBaseParam.getUsername(),loginBaseParam.getAcctId(),getLoginParamKey(loginBaseParam));
        }
    }

    public List<String> loginToGetCookie(ErpLoginParam loginParam) {
        String url = ErpUrlConstants.loginUrl;
        HttpEntity<String> loginDataEntity = new HttpEntity<>(JSON.toJSONString(loginParam), getJsonHeader(null));
        ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(url, loginDataEntity, String.class);
        try {
            if (stringResponseEntity.getStatusCode() != HttpStatus.OK) {
                throw new ErpAccessException("访问Erp系统失败，返回的statusCode为 " + stringResponseEntity.getStatusCodeValue());
            }
            LoginResponseBody loginResponseBody = JSONObject.parseObject(stringResponseEntity.getBody(), LoginResponseBody.class);
            assert loginResponseBody != null;
            if (ErpLoginResultCodeEnum.isLoginFail(loginResponseBody.getLoginResultType())) {
                throw new ErpLoginFailException("登录失败，原因：" + ErpLoginResultCodeEnum.getReasonByCode(loginResponseBody.getLoginResultType()));
            }
            List<String> cookie = stringResponseEntity.getHeaders().get("Set-Cookie");
            if (CollectionUtils.isEmpty(cookie)) {
                throw new ErpLoginFailException("获取到cookies为空");
            }
            logger.info("账号：{}登录erp数据中心 {} 成功！",loginParam.getUsername(),loginParam.getAcctId());
            this.setCookieToRedis(loginParam,cookie);
            return cookie;
        } catch (Exception e) {
            logger.error("登录ERP失败");
            logger.error("发送数据：{}",loginDataEntity);
            logger.error("返回内容：{}", stringResponseEntity);
            throw new ErpLoginFailException();
        }
    }

    private String postForEntity(String url, HttpEntity requestData){
        ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(url, requestData, String.class);
        if (!stringResponseEntity.getStatusCode().is2xxSuccessful()) {
            logger.error("访问erp失败！状态码为：{}", stringResponseEntity.getStatusCodeValue());
            throw new ErpAccessException();
        }
        return stringResponseEntity.getBody();
    }
    private boolean isCookieExpire(String retData){
        return retData.contains(COOKIE_EXPIRE_GENERAL_FLAG) || isCookieExpireWhenSave(retData);
    }
    private boolean isCookieExpireWhenSave(String retData){
        for (String flag : COOKIE_EXPIRE_FLAG_LIST_WHEN_SINGLE_SAVE) {
            if (retData.contains(flag)) {
                return true;
            }
        }
        return false;
    }

    public String postForJsonString(String url, AbstractErpParam data) {
        ErpLoginParam erpLoginParam = data.getErpLoginParam();
        // 避免序列化到最后的结果中
        data.setErpLoginParam(null);
        List<String> cookies = login(erpLoginParam, true);
        HttpEntity<String> requestData;
        String sendData = JSON.toJSONString(data);
        logger.info("提交json串：");
        logger.info(sendData);
        requestData = new HttpEntity<>(sendData, getJsonHeader(cookies));
        String retData = postForEntity(url, requestData);
        if (StringUtils.isEmpty(retData)) {
            logger.error("返回数据为空，返回数据：{}", retData);
            throw new ErpResponseNotMeetExpectation("返回数据为空");
        }
        //如果失败，则重试
        if (isCookieExpire(retData)) {
            logger.info("返回数据：{}", retData);
            logger.info("账户{}在数据中心 {} 的cookie已经过期，现在进行重试！", erpLoginParam.getUsername(), erpLoginParam.getAcctId().toString());
            List<String> cookiesRelogin = login(erpLoginParam, false);
            requestData = new HttpEntity<>(sendData, getJsonHeader(cookiesRelogin));
            retData = postForEntity(url, requestData);
            if (StringUtils.isEmpty(retData)) {
                logger.error("返回数据为空，返回数据：{}", retData);
                throw new ErpAccessException("返回数据为空");
            }
            if (isCookieExpire(retData)) {
                throw new ErpLoginFailException();
            }
        }
        //直接返回数组，jsonObject 无法解析，直接返回
        String successFlag = "[";
        if (!retData.startsWith(successFlag)) {
            //查看是否请求有发生问题
            JSONObject errJson = JSONObject.parseObject(retData);
            Object message = errJson.get("Message");
            if (message != null) {
                logger.error("访问erp出错！错误信息：{},发送的数据：{}", message, requestData.toString());
                throw new ErpResponseNotMeetExpectation();
            }
        }
        return retData;
    }

    public JSONObject postForJsonObject(String url, AbstractErpParam data) {
        return JSONObject.parseObject(this.postForJsonString(url, data));
    }
}
